/*
 * Copyright (c) 2021 Futurewei Technologies, Inc.
 *
 * clang2mpl is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the
 * Mulan PSL v2 for more details.
 */
// RUN: %clang2mpl --ascii --verify %s -- -Wno-unused-value --target=aarch64-linux-elf
// RUN: cat %m | %FileCheck %s

// CHECK: type $A <struct {
// CHECK-NEXT:   @x i32,
// CHECK-NEXT:   @y i32}>
struct A {
  int x;
  int y;
};

// CHECK: type $B <struct {
// CHECK-NEXT:   @x :1 u32,
// CHECK-NEXT:   @y :3 u32}>
struct B {
  unsigned int x: 1;
  unsigned int y : 3;
};

// CHECK: type $C <struct {
// CHECK-NEXT:   @a <$A>}>
struct C {
  struct A a;
};

// CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
// CHECK: var $gA <$A>
struct A gA;
// CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
// CHECK: var $gB <$B>
struct B gB;
// CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
// CHECK: var $gC <$C>
struct C gC;

int get();

void foo() {
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: eval (dread i32 $gA 1)
  gA.x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign $gA 2 (constval i32 4)
  gA.y = 4;
  // CHECK: LOC 2 [[# @LINE + 3 ]]{{$}}
  // CHECK-NEXT: callassigned &get () { dassign %_result[[#RES1:]] 0 }
  // CHECK-NEXT: dassign $gA 1 (dread i32 %_result[[#RES1]])
  gA.x = get();
}

void bar(struct A *p) {
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: eval (iread i32 <* <$A>> 1 (dread a64 %p))
  p->x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: iassign <* <$A>> 2 (dread a64 %p, constval i32 4)
  p->y = 4;
  // CHECK: LOC 2 [[# @LINE + 3 ]]{{$}}
  // CHECK-NEXT: callassigned &get () { dassign %_result[[#RV0:]] 0 }
  // CHECK-NEXT: iassign <* <$A>> 1 (dread a64 %p, dread i32 %_result[[#RV0]])
  p->x = get();
}


void baz(struct C *c) {
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: eval (dread i32 $gC 2)
  gC.a.x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign $gC 3 (constval i32 42)
  gC.a.y = 42;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: eval (iread i32 <* <$C>> 2 (dread a64 %c))
  c->a.x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: iassign <* <$C>> 3 (dread a64 %c, constval i32 7)
  c->a.y = 7;
}

void bitfield() {
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: eval (dread u32 $gB 1)
  gB.x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dassign $gB 2 (constval u32 4)
  gB.y = 4;
}

struct D {
  int x;
  int y;
};

struct E {
  int a;
  struct D d1;
  struct D d2;
  int b;
};

void embedded() {
  struct E e;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dread i32 %e 1
  e.a;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dread agg %e 2
  e.d1;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dread agg %e 5
  e.d2;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dread i32 %e 3
  e.d1.x;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dread i32 %e 7
  e.d2.y;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dread i32 %e 8
  e.b;
}

struct F { int i; int j; };
struct G { struct F *a; struct F *b; };
struct H { struct G *c; struct F *d; };

void embedded2() {
  struct H h;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: iread i32 <* <$F>> 1 (iread a64 <* <$G>> 1 (dread a64 %h 1))
  h.c->a->i;
}

struct I {
  int a;
  struct F f[10];
  int b;
};

void arrayOfStructs() {
  struct I i;
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: dread i32 %i 5
  i.b;
}

struct J {
  unsigned long p;
  int q;
};

struct K {
  struct J *j;
  int a;
};

void castField(struct K *k) {
  // CHECK: LOC 2 [[# @LINE + 2 ]]{{$}}
  // CHECK-NEXT: iread a64 <* <$J>> 1 (iread a64 <* <$K>> 1 (dread a64 %k))
  (int *)(k->j->p);
}
